using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Ink;
using System.Windows.Input;
using System.Diagnostics;
using System.IO;
using System.Xml.Serialization;

using WozLib.UI.Ink.SketchProperties;
using System.Windows;
using System.Windows.Media;
using WozLib.UI.Ink.SketchObjects;

namespace WozLib.UI.Ink.SketchElements
{
  public class SketchStroke : Stroke, ISketchObject
  {

    public static SketchStroke FromStrokeWozObject(StrokeSketchObject strokeWozObject)
    {
      SketchPropertyCollection wozPropertyCollection = new SketchPropertyCollection();
      wozPropertyCollection.WozProperties = strokeWozObject.WozProperties;

      if (wozPropertyCollection.GetProperty(WozPropertyType.Ink) != null)
      {
        StrokeSketchProperty strokeWozProperty = wozPropertyCollection.GetProperty(WozPropertyType.Ink) as StrokeSketchProperty;

        return new SketchStroke(strokeWozProperty.Data.StylusPoints, strokeWozProperty.Data.DrawingAttributes, strokeWozObject.WozID, wozPropertyCollection);
      }
      else
      {
        return null;
      }
    }

    public SketchStroke(Stroke stroke) : this( stroke.StylusPoints,stroke.DrawingAttributes)
    {
    }

    public SketchStroke(Stroke stroke, SketchObjectRegistry wozObjectRegistry)
      : this(stroke.StylusPoints, stroke.DrawingAttributes, wozObjectRegistry)
    {
    }

    public SketchStroke(StylusPointCollection points, DrawingAttributes drawingAttributes)
      : this(points, drawingAttributes, null)
    {
    }

    public SketchStroke(StylusPointCollection points, DrawingAttributes drawingAttributes, SketchObjectRegistry wozObjectRegistry)
      : base(points, drawingAttributes)
    {
      // register this object
      if (wozObjectRegistry != null)
      {
        wozObjectRegistry.RegisterObject(this);
      }
      else
      {
        
      }

      // Set all of our WozPropertyCollection properties

      // Set ink property
      StrokeSketchProperty strokeWozProperty = new StrokeSketchProperty(this);
      _WozPropertyCollection.SetProperty(strokeWozProperty);

      // Set color property
      ColorSketchProperty colorWozProperty = new ColorSketchProperty(this.DrawingAttributes.Color, WozPropertyType.StrokeColor);
      _WozPropertyCollection.SetProperty(colorWozProperty);

      BoundsSketchProperty wozBoundsProperty = new BoundsSketchProperty(this.GetBounds());
      _WozPropertyCollection.SetProperty(wozBoundsProperty);

      InkThicknessSketchProperty WozInkThicknessProperty = new InkThicknessSketchProperty(new Thickness(this.DrawingAttributes.Width));
      _WozPropertyCollection.SetProperty(WozInkThicknessProperty);

      // subscribe to events
      this.StylusPointsChanged += new EventHandler(WozStroke_StylusPointsChanged);
      this.StylusPointsReplaced += new StylusPointsReplacedEventHandler(WozStroke_StylusPointsReplaced);
      this.DrawingAttributesChanged += new PropertyDataChangedEventHandler(WozStroke_DrawingAttributesChanged);
      this.DrawingAttributesReplaced += new DrawingAttributesReplacedEventHandler(WozStroke_DrawingAttributesReplaced);


      // how to save strokes
      //StrokeCollection strokes = new StrokeCollection();
      //strokes.Add(this);
      //MemoryStream memoryStream = new MemoryStream();
      //strokes.Save(memoryStream);
      //byte[] strokeBytes = memoryStream.ToArray();


      // how to serialize and deserialize something
      //DateTime toSerialize = DateTime.Now;

      //// Insert code to set properties and fields of the object.
      //XmlSerializer mySerializer = new XmlSerializer(typeof(DateTime));

      //// To write to a file, create a StreamWriter object.
      //StringBuilder sb = new StringBuilder();
      //StringWriter sw = new StringWriter(sb);
      //mySerializer.Serialize(sw, toSerialize);
      //sw.Close();

      //Debug.WriteLine("Serialized " + toSerialize.ToString() + ": " + sb.ToString());

      //StringReader sr = new StringReader(sb.ToString());
      //DateTime unserialized = (DateTime)mySerializer.Deserialize(sr);

      //Debug.WriteLine("Deserialized " + unserialized.ToString());
    }

    void WozStroke_DrawingAttributesReplaced(object sender, DrawingAttributesReplacedEventArgs e)
    {
    }

    void WozStroke_DrawingAttributesChanged(object sender, PropertyDataChangedEventArgs e)
    {
    }

    public SketchStroke(StylusPointCollection points, DrawingAttributes drawingAttributes, long id, SketchPropertyCollection wozPropertyCollection)
      : this(points, drawingAttributes, id, wozPropertyCollection, null)
    {
    }

    public SketchStroke(StylusPointCollection points, DrawingAttributes drawingAttributes, long id, SketchPropertyCollection wozPropertyCollection, SketchObjectRegistry wozObjectRegistry)
      : base(points, drawingAttributes)
    {
      // assign our ID before we doing anything else
      this._WozID = id;

      // overwrite our blank collection with the provided one
      this._WozPropertyCollection = wozPropertyCollection;

      // use our properties to make any changes to our underlying objects
      // Note we don't need to peek at the Ink property because it get's passed into our base object during construction

      // Set color property
      if (_WozPropertyCollection.GetProperty(WozPropertyType.StrokeColor) != null)
      {
        SetProperty(_WozPropertyCollection.GetProperty(WozPropertyType.StrokeColor));
      }

      if (_WozPropertyCollection.GetProperty(WozPropertyType.Bounds) != null)
      {
        SetProperty(_WozPropertyCollection.GetProperty(WozPropertyType.Bounds));
      }

      if (_WozPropertyCollection.GetProperty(WozPropertyType.InkThickness) != null)
      {
        SetProperty(_WozPropertyCollection.GetProperty(WozPropertyType.InkThickness));
      }


      // register this object
      if (wozObjectRegistry != null)
      {
        wozObjectRegistry.RegisterObjectWithExisitingId(this);
      }

      // subscribe to events
      this.StylusPointsChanged += new EventHandler(WozStroke_StylusPointsChanged);
      this.StylusPointsReplaced += new StylusPointsReplacedEventHandler(WozStroke_StylusPointsReplaced);
    }

    void WozStroke_StylusPointsReplaced(object sender, StylusPointsReplacedEventArgs e)
    {
      //Debug.WriteLine("WozStroke_StylusPointsReplaced");
    }

    void WozStroke_StylusPointsChanged(object sender, EventArgs e)
    {
      //Debug.WriteLine("WozStroke_StylusPointsChanged");
    }

    #region IWozObject Members

    // our private way of holding an ID
    private long _WozID = long.MinValue;

    public long WozID
    {
      get 
      {
        return _WozID;
      }
      set
      {
        _WozID = value;
      }
    }

    // our private way of holding properties
    protected SketchPropertyCollection _WozPropertyCollection = new SketchPropertyCollection();

    // You should update your properties to reflect the actualy state of your underlying object
    // i.e if your current color property says Black, but your stroke actually had its color
    // changed to Red recently - change the color property to red
    public void SyncProperties()
    {
      SyncProperties(_WozPropertyCollection);
    }

    // You should update your properties to reflect the actualy state of your underlying object
    // i.e if your current color property says Black, but your stroke actually had its color
    // changed to Red recently - change the color property to red
    protected void SyncProperties(SketchPropertyCollection wozPropertyCollection)
    {
      // first we'll set our ink property correctly
      StrokeSketchProperty strokeWozProperty = wozPropertyCollection.GetProperty(WozPropertyType.Ink) as StrokeSketchProperty;
      strokeWozProperty.Data = new Stroke(this.StylusPoints, this.DrawingAttributes);

      // Set color property
      ColorSketchProperty colorWozProperty = wozPropertyCollection.GetProperty(WozPropertyType.StrokeColor) as ColorSketchProperty;
      colorWozProperty.Data = this.DrawingAttributes.Color;

      BoundsSketchProperty wozBoundsProperty = wozPropertyCollection.GetProperty(WozPropertyType.Bounds) as BoundsSketchProperty;
      wozBoundsProperty.Data = this.GetBounds();

      InkThicknessSketchProperty WozInkThicknessProperty = wozPropertyCollection.GetProperty(WozPropertyType.InkThickness) as InkThicknessSketchProperty;
      WozInkThicknessProperty.Data = new Thickness(this.DrawingAttributes.Width);
      
    }

    // Set the property, but also make the corresponding change to the underlying object
    // i.e. if the color property is set on a stroke, actually change the drawing attributes
    public void SetProperty(WozLib.UI.Ink.SketchProperties.SketchProperty wozProperty)
    {
      _WozPropertyCollection.SetProperty(wozProperty);

      UpdateProperty(wozProperty);
    }

    private void UpdateProperty(WozLib.UI.Ink.SketchProperties.SketchProperty wozProperty)
    {
      try
      {
        // Figure out what we should with this property
        switch (wozProperty.WozPropertyType)
        {
          case WozPropertyType.Ink:
            // well let's update our ink shall we
            // first cast to the right kind of property
            StrokeSketchProperty strokeWozProperty = wozProperty as StrokeSketchProperty;

            // update our drawing attributes
            // This could actually be problematic if we ran it after a color sync - we might overwrite a color setting change
            // ToDo: probably comment this out
            this.DrawingAttributes = strokeWozProperty.Data.DrawingAttributes;

            // clear our stylus points and update with property's points
            this.StylusPoints = strokeWozProperty.Data.StylusPoints;
            break;

          case WozPropertyType.StrokeColor:
            this.DrawingAttributes.Color = (wozProperty as ColorSketchProperty).Data;
            break;

          case WozPropertyType.Bounds:
            // don't know how right now
            break;

          case WozPropertyType.InkThickness:
            // for now assume uniform thickness
            this.DrawingAttributes.Width = (wozProperty as InkThicknessSketchProperty).Data.Left;
            this.DrawingAttributes.Height = (wozProperty as InkThicknessSketchProperty).Data.Left;
            break;

          default:
            // This shouldn't happen
            Debug.WriteLine(this.ToString() + " doesn't know what to do with a " + wozProperty.WozPropertyType.ToString());
            break;
        }
      }
      catch (Exception ex)
      {
        Debug.WriteLine("SketchLine: Property Setting Failed: " + ex.ToString());
      }
    }

    public void SetProperty(IEnumerable<WozLib.UI.Ink.SketchProperties.SketchProperty> wozProperties)
    {
      foreach (SketchProperty wozProperty in wozProperties)
      {
        SetProperty(wozProperty);
      }
    }

    // just return the specified property
    public SketchProperty GetProperty(WozPropertyType wozPropertyType)
    {
      return _WozPropertyCollection.GetProperty(wozPropertyType);
    }

    // WozPropertyCollection makes a copy for us
    public IEnumerable<SketchProperty> GetProperties()
    {
      return _WozPropertyCollection.WozProperties;
    }

    // Make sure we create the WozObject from the appropriate derived class (StrokeWozObject)
    public SketchObject GetWozObject()
    {
      return new StrokeSketchObject(WozID, GetProperties());
    }

    #endregion

    #region IWozObject Members


    public bool ShouldErase(Point pointRelativeToUie)
    {
      // erase me with inkcanvas built in eraser!
      return false;
    }

    #endregion
  }
}
